package org.liang.transfer.sftp;

import java.io.File;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.SftpException;

/**
 * SFTP上传器.
 * 
 * <pre>
 * 整合SFTP上传实现多有代码。
 * 此类仅向{@link SFTP}开放访问
 * </pre>
 * 
 * @author L.Yang
 * @version v1.0 2016年4月11日 下午3:46:28
 */
class SFTPUploader {

    private static final Logger LOGGER = LoggerFactory.getLogger(SFTPUploader.class);

    private static final int EXCEPTION_FILE_ALREADY_EXISTS = 11;

    private ChannelSftp sftp = null;

    SFTPUploader(ChannelSftp sftp) {
        super();
        this.sftp = sftp;
    }

    /**
     * 上传文件到服务器.
     * 
     * <pre>
     * 如果是目录则进行递归上传，将目录下所有文件夹、文件上传到服务器。
     * </pre>
     * 
     * @param remote
     *            远程FTP服务器文件路径
     * @param local
     *            本地存储文件的完整路径（包含文件名）
     */
    void upload(String remote, String local) {
        try {
            intoWorkDirectory(remote);
            uploadFile(remote, local);
        } catch (SftpException e) {
            LOGGER.error("SFTP 上传失败 :{}", ExceptionUtils.getStackTrace(e));
        }
    }

    private void intoWorkDirectory(String remote) throws SftpException {
        String path = FilenameUtils.getFullPath(remote);
        String[] dirs = path.split("/");
        for (String dir : dirs) {
            if (StringUtils.isEmpty(dir))
                continue;
            mkd(dir);
            sftp.cd(dir);
        }
        LOGGER.trace("进入工作目录 : {}", path);
    }

    private void uploadFile(String remote, String local) throws SftpException {
        File localfile = new File(local);
        if (localfile.isDirectory()) {
            recursiveupload(remote, localfile);
        } else {
            storeFile(remote, localfile.getAbsolutePath());
        }
    }

    private void recursiveupload(String remote, File directory) throws SftpException {
        LOGGER.trace("进入子文件夹： {} ", remote);
        for (File file : directory.listFiles()) {
            if (file.isDirectory()) {
                String path = file.getName();
                mkd(path);
                sftp.cd(path);
                recursiveupload(remote + path + "//", file);
            } else {
                storeFile(remote, file.getAbsolutePath());
            }
        }
        sftp.cd("..");
        LOGGER.trace("返回上级文件夹 ");
    }

    /**
     * 将本地文件上传到远程服务器上.
     * 
     * @param remote
     *            远程文件地址
     * @param localfile
     *            本地文件路径
     * @throws SftpException sftp 异常
     * @see {@link ChannelSftp#put(String, String, int)}
     */
    private void storeFile(String remote, String localfile) throws SftpException {
        String dst = FilenameUtils.getFullPath(remote) + FilenameUtils.getName(localfile);
        sftp.put(localfile, dst, ChannelSftp.OVERWRITE);
        LOGGER.trace("文件上传完成： {}, {}", new Object[] {dst, localfile});
    }

    /**
     * 创建目录.
     * 
     * <pre>
     * 如果已存在则忽略
     * </pre>
     * 
     * @param path
     *            路径
     * @throws SftpException sftp 异常
     * @see {@link ChannelSftp#mkdir(String)}
     */
    private void mkd(String path) throws SftpException {
        try {
            sftp.mkdir(path);
        } catch (SftpException e) {
            if (e.id != EXCEPTION_FILE_ALREADY_EXISTS) {
                throw e;
            }
        }
    }
}
